-
-
Notifications
You must be signed in to change notification settings - Fork 219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(#8437): enable useUnknownInCatchVariables rule in tsconfig #9646
base: master
Are you sure you want to change the base?
Conversation
const status = getProperty(error, 'status'); | ||
if (status === 0) { // Offline status |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sold on this change yet... I agree we shouldn't assume anything thrown/caught is an Error
object and the unknown
type is best for this purpose because it forces us to do proper validation in the catch
clause. But this change barely gets us halfway there: it checks that the property exists but it doesn't help with type-safety.
I don't think a generic lib file can solve this problem on its own. I think this sort of validation should be limited to its respective catch
clause. For example here this could look like:
import { HttpErrorResponse } from '@angular/common/http';
try {
// ...
} catch {
if (error instanceof HttpErrorResponse) {
const status = error.status;
// ^? number
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙌 This is just the kind of push back I was hoping for! I am very interested in digging in deeper here to find the best solution.
The challenge I see with doing something like error instanceof HttpErrorResponse
in existing code is that we would have to be absolutely certain of what is in scope for the try-block and what kind of errors might be thrown. Taking this code as a good example, there is quite a bit of logic wrapped in the try-block and I am not confident that anything that was being thrown with a status
property, is definitely going to be instanceof HttpErrorResponse
. For example, even with a local Pouch db, if you call get
with a docId that does not exist, it will throw an error with status: 404
, but I do not think it will be a HttpErrorResponse
...
This was the main reason I settled on this half-baked pattern matching approach. 😬
his change barely gets us halfway there: it checks that the property exists but it doesn't help with type-safety.
100% agree. But, I really think that if we are going to do nested type-checking, we should revisit the idea of adding a schema validation library.... 😬 I have found effect/schema
to be super powerful in chtoolbox and I think we could reduce a lot of manual logic that we have by pulling in something like that. (FTR, not saying we go with effect/schema
here in cht-core.... 😅 Zod, seems like the default option, but I find https://github.com/fabian-hiller/valibot to be very interesting as well, particularly with its claims of superior tree-shaking....)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a simple side-by-side comparison of zod
vs valibot
for evaluating the schema of this error. The code is nearly identical:
zod
:
const MyErrorSchema = z.object({
status: z.number().optional(),
});
const myError = MyErrorSchema.parse(error);
valibot
:
const MyErrorSchema = v.object({
status: v.optional(v.number()),
});
const myError = v.parse(MyErrorSchema, error);
Then, I used npm run build
to build the prod assets (with tree-shaking) and compared the sizes of api/build/static/webapp
:
- Current:
8384K
- Zod:
8444K
-+60K
- Valibot:
8388K
-+4K
This is inline with valibot's claims to be significantly more efficient than Zod because the structure of the project is optimized for tree shaking.
IMHO +4K
is worth it for sane schema validation. In reality, we should be able to replace a lot of janky manual code checks with valibot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Mokhtar does have a point in finding the object type of err
. It is the general pattern in other programming languages as well. For eg:
- Java
try {
// Code that may throw an exception
} catch (IOException e) {
// Handle IOException
} catch (NumberFormatException e) {
// Handle NumberFormatException
} catch (Exception e) {
// Handle any other exceptions
}
- python
try:
n = 0
res = 100 / n
except ZeroDivisionError:
print("You can't divide by zero!")
except ValueError:
print("Enter a valid number!")
else:
print("Result is", res)
finally:
print("Execution complete.")
I think we should apply these here as well and I agree that we probably won't know what kind of error the error object is. We would check for the most likely errors in the conditionals. If we are not sure what the error object would be a type of then, the getProperty
function could be used in the default clause. For eg: when making a network call to an endpoint it's most likely not gonna throw a FileNotFound kind of error.
I'm not sure if zod and valibot are required here.
# Conflicts: # webapp/src/ts/modules/tasks/tasks.component.ts # webapp/src/ts/services/user-contact.service.ts # webapp/tests/karma/ts/services/training-cards.service.spec.ts
@sugat009 would love your input on this PR. I think I would rather not merge things as they are currently, but am very interested in continuing the discussion Mokhtar started regarding a schema validation library. This PR provides a really good example use-case for schema validation, but I would also be interested in making heavy use of validation in the cht-datasource code.... |
Description
#8437
The
useUnknownInCatchVariables
rule basically just makes it so that the TS compiler considers errors in acatch
block to have theunknown
type (instead of theany
type). This necessitates proper type checking when referencing properties on the error. In general I think the "correct" way to handleunknown
types is to establish a formal expected type for the value and then create a type guard (or type assertion) to make sure we have the data we expect. However, forcatch
blocks in particular this is tricky since really the error could be anything, coming from anywhere. For the most part ourcatch
logic just want to grab a (possiblyundefined
) property value off of of the error and then move on. It does not seem super useful to make the error a formal data type. With that in mind, my approach here was just to create a couple utility functions to make it easier to read properties from anunknown
in a type-safe way. Nothing fancy, but it works with minimal additional code needed in thecatch
blocks. However, I am far from being convinced this is the best approach, so I am very open to other suggestions for better ways to handle this.Since, we do not have any formal schema validation library, I had to roll my own logic here for checking the properties. I had trouble figuring out where in the webapp code base I should put these functions. All the other code seems to be organized/categorized by specific Angular features with no where that I could just add some general-purpose utility functions. So, I ended up creating
webapp/src/ts/libs
and adding theschema.ts
file to hold my functions. Once again, very open to alternate suggestions here...Then I had the issue of how to test the
webapp/src/ts/libs
. It does not need karma tests since it is not Angular-specific. Instead I just wanted plan mocha tests in TS files. Thankfully, in a separate branch I had already been working on adding support for this. So, I pulled that code in here. In general, this is all very excessive just to get these two little schema functions. However, my thought is that this is not so much a one-off as it is a new core pattern that can be leveraged by more code in the future. (As noted, I already have plans to add additional tests like this in a separate branch....) Main thing is that it is providing the opportunity for adding code/tests that is not tightly coupled to Angular.Code review checklist
Compose URLs
If Build CI hasn't passed, these may 404:
License
The software is provided under AGPL-3.0. Contributions to this project are accepted under the same license.